package com.icesoft.core.common.util;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.cert.CertIOException;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;

import javax.security.auth.x500.X500Principal;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;


/**
 * 自制ca证书工具类
 */
public class CaCreateUtil {

    /***
     * 根据根证书文件路径签发证书
     * @param rootPrivateKeyPath 根证书私钥路径
     * @param rootCrtPath 根证书路径
     * @param outPath 证书保存路径
     * @param params 证书认证信息，O=单位名称,L=城市,S=省份,C=国家
     * @param hostName 证书使用的域名
     * @param password KeyStore pkcs12文件密码
     *
     */
    public static void createCert(String rootPrivateKeyPath, String rootCrtPath, String outPath, String params, String hostName, String password) throws IOException, OperatorCreationException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
        PEMKeyPair pemKeyPair = PemFileUtils.readPemKeyPair(rootPrivateKeyPath);
        KeyPair keyPair = KeyPairUtils.convert(pemKeyPair);
        PrivateKey privateKey = keyPair.getPrivate();
        X509CertificateHolder holder = new X509CertificateHolder(FileUtil.readByteArray(new File(rootCrtPath)));
        createCert(privateKey, holder, outPath, params, hostName, password);
    }

    /***
     * 创建自签名根证书
     * @param outPath 根证书保存路径
     * @param rootParams 根证书认证信息，O=单位名称,L=城市,S=省份,C=国家
     * @param password KeyStore pkcs12文件密码
     */
    public static void createRootCert(String outPath, String rootParams, String password) throws IOException, OperatorCreationException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
        createCert((PrivateKey) null, null, outPath, rootParams, null, password);
    }

    /***
     * 根据根证书签发证书
     * @param rootPrivateKey 根证书私钥
     * @param holder 根证书认证信息
     * @param outPath 证书保存路径
     * @param params 证书认证信息，O=单位名称,L=城市,S=省份,C=国家
     * @param hostName 证书使用的域名或者IP
     * @param password KeyStore pkcs12文件密码
     *
     */
    public static void createCert(PrivateKey rootPrivateKey, X509CertificateHolder holder, String outPath, String params, String hostName, String password) throws IOException, OperatorCreationException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
        outPath = appendEndPath(outPath);
        PEMKeyPair pemKeyPair = KeyPairUtils.createPemKeyPair();
        KeyPair keyPair = KeyPairUtils.convert(pemKeyPair);
        PemFileUtils.writeKeyPairToFile(outPath + "private.key", keyPair);
        if (rootPrivateKey == null) {
            rootPrivateKey = keyPair.getPrivate();
        }
        PKCS10CertificationRequest pkcs10CertificationRequest = createCsr(keyPair, params);
        X500Name rootName = holder == null ? pkcs10CertificationRequest.getSubject() : holder.getSubject();
        X509Certificate cert = createCertificate(rootPrivateKey, rootName, pkcs10CertificationRequest, hostName, DateUtils.addYear(new Date(), 10));
        FileUtil.writeByteToFile(new File(outPath + "ca.crt"), cert.getEncoded());
        PemFileUtils.writeCertificateToFile(outPath + "crt.pem", cert);
        createPkcs12(outPath, keyPair.getPrivate(), cert, password);
    }


    /**
     * 创建pkcs12标准的KeyStore文件
     *
     * @param outPath    保存路径
     * @param privateKey 证书私钥
     * @param password   KeyStore pkcs12文件密码
     */
    public static void createPkcs12(String outPath, PrivateKey privateKey, Certificate cert, String password) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
        outPath = appendEndPath(outPath);
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(null, null);
        password = password == null ? "" : password;
        char[] pass = password.toCharArray();
        keyStore.setKeyEntry("", privateKey, pass, new Certificate[]{cert});
        keyStore.store(new FileOutputStream(outPath + "keyStore.p12"), pass);
        FileUtil.writeByteToFile(new File(outPath, "password"), password.getBytes());
    }

    /**
     * 创建csr对象
     *
     * @param keyPair 密钥对
     * @param params  CN=域名,O=单位名称,L=城市,S=省份,C=国家
     */
    public static PKCS10CertificationRequest createCsr(KeyPair keyPair, String params) throws OperatorCreationException {
        X500Principal principal = new X500Principal(params);
        PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(principal, keyPair.getPublic());
        ContentSigner signGen = new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate());
        return builder.build(signGen);
    }

    private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();

    /**
     * 通过csr创建x509证书文件
     *
     * @param rootName                   根证书信息
     * @param rootPrivateKey             根证书密钥
     * @param pkcs10CertificationRequest csr
     * @param endTime                    证书到期时间
     * @param hostname                   证书使用的域名或者IP
     */
    public static X509Certificate createCertificate(PrivateKey rootPrivateKey, X500Name rootName, PKCS10CertificationRequest pkcs10CertificationRequest, String hostname, Date endTime) throws CertificateException, OperatorCreationException, CertIOException {
        // X500Name root = rootName == null ? pkcs10CertificationRequest.getSubject() : new X500Name(rootName);
        final Date start = new Date();
        final X509v3CertificateBuilder certificateBuilder = new X509v3CertificateBuilder(rootName,
                new BigInteger(10, new SecureRandom()), //Choose something better for real use
                start,
                endTime,
                pkcs10CertificationRequest.getSubject(),
                pkcs10CertificationRequest.getSubjectPublicKeyInfo()
        );
        if (hostname != null) {
            int hostType = GeneralName.dNSName;
            if (StringUtils.isNumeric(String.valueOf(hostname.charAt(0)))) {
                hostType = GeneralName.iPAddress;
            }
            GeneralNames subjectAltName = new GeneralNames(new GeneralName(hostType, hostname));
            certificateBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltName);
        }
        ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA").setProvider(PROVIDER).build(rootPrivateKey);
        final X509CertificateHolder holder = certificateBuilder.build(signer);
        return new JcaX509CertificateConverter().setProvider(PROVIDER).getCertificate(holder);
    }

    private static String appendEndPath(String path) {
        if (!path.endsWith("/")) {
            path = path + "/";
        }
        return path;
    }

}
